package com.zhu.classfile;

import com.zhu.classfile.attribute.AttributeInfo;
import com.zhu.classfile.attribute.factory.AttributeInfoFactory;
import com.zhu.classfile.constant.ConstantInfo;
import com.zhu.classfile.constant.ConstantPool;
import com.zhu.classfile.constant.base.ConstantClassInfo;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * ClassBinary: .class 文件的内存映像
 * 它不是运行时类，而是 class 文件的“二进制结构化表示”
 */
public class ClassBinary {

    /**
     * 魔数：必须是 0xCAFEBABE
     * 用于快速识别是否为有效的 class 文件
     */
    private int magic;

    /**
     * 次版本号（minor_version）
     * 通常为 0
     */
    private int minorVersion;

    /**
     * 主版本号（major_version）
     * Java 8 -> 52, Java 17 -> 61
     */
    private int majorVersion;
    /**
     * 常量池（Constant Pool）长度
     */
    private int   constantPoolCount;
    /**
     * 常量池（Constant Pool）
     * 存储字面量、符号引用、类名、方法名等
     * 所有后续结构都依赖它
     */
    private ConstantPool constantPool;

    /**
     * 访问标志（access_flags）
     * 如：ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_INTERFACE 等
     */
    private int accessFlags;

    /**
     * 指向常量池的索引，表示本类的全限定名
     * 类型：CONSTANT_Class_info
     */
    private int thisClass;

    /**
     * 指向常量池的索引，表示父类的全限定名
     * 类型：CONSTANT_Class_info
     * Object 类为 0
     */
    private int superClass;

    /**
     * 实现的接口数量
     */
    private int interfacesCount;

    /**
     * 接口表：每个元素是指向常量池的索引
     * 类型：CONSTANT_Class_info
     * Go 类型	Java 对应	      说明
     * uint8	  int	          虽然 byte 是 8 位，但有符号，需转无符号
     * uint16	  int	          short会溢出，int 最安全
     * uint32	  long	          int会溢出
     * []uint16	i  nt[]	          数组也用 int[]
     * *MemberInfo	MemberInfo	  Go的指针 → Java 的引用
     * []AttributeInfo	AttributeInfo[]	切片 → 数组
     */
    private int[] interfaces;

    /**
     * 字段数量
     */
    private int fieldsCount;

    /**
     * 字段表数组
     * 每个字段包含名称、描述符、访问标志、属性等
     */
    private MemberInfo[] fields;

    /**
     * 方法数量
     */
    private int methodsCount;

    /**
     * 方法表数组
     * 每个方法包含名称、描述符、访问标志、属性（如 Code 属性）
     */
    private MemberInfo[] methods;


    /**
     * 属性数量（位于类级别的属性，如 SourceFile、InnerClasses）
     */
    private int attributesCount;

    /**
     * 类级别的属性表
     * 如：SourceFile, Signature, Deprecated 等
     */
    private AttributeInfo[] attributes;

    private ClassBinary(){

    }

    /**
     * 全参数构造方法
     * 用于反射、序列化或测试场景
     */
    public ClassBinary(
            int magic,
            int minorVersion,
            int majorVersion,
            int constantPoolCount,
            ConstantPool constantPool,
            int accessFlags,
            int thisClass,
            int superClass,
            int interfacesCount,
            int[] interfaces,
            int fieldsCount,
            MemberInfo[] fields,
            int methodsCount,
            MemberInfo[] methods,
            int attributesCount,
            AttributeInfo[] attributes) {
        this.magic = magic;
        this.minorVersion = minorVersion;
        this.majorVersion = majorVersion;
        this.constantPoolCount = constantPoolCount;
        this.constantPool = constantPool;
        this.accessFlags = accessFlags;
        this.thisClass = thisClass;
        this.superClass = superClass;
        this.interfacesCount = interfacesCount;
        this.interfaces = interfaces;
        this.fieldsCount = fieldsCount;
        this.fields = fields;
        this.methodsCount = methodsCount;
        this.methods = methods;
        this.attributesCount=attributesCount;
        this.attributes = attributes;
    }

    /**
     * 把字节数组转换成ClassBinary二进制镜像
     * parseClassDataToClassBinary 没有在常量池中生成 Methodref，是因为 这个方法在当前类的字节码中从未被“引用”。
     * JVM 的常量池只包含那些“被其他地方引用”的符号信息，而不是“类中定义的所有方法”。
     * VM 常量池的设计原则是：“按需存放” —— 只存放那些在字节码中被 invokevirtual、invokestatic、invokespecial 等指令 实际引用 到的方法、字段或类。
     * 它只是“存在”于类中，供外部调用，但 当前类的字节码内部没有调用它。
     */
    public static ClassBinary parseClassDataToClassBinary(byte[] data){
        ByteBuffer buffer = ByteBuffer.wrap(data);
        buffer.order(ByteOrder.BIG_ENDIAN);
        //  读魔数 + 校验
        int magic = readAndCheckMagic(buffer);
        // 读版本号（连续读两个 u2）
        int minorVersion = Short.toUnsignedInt(buffer.getShort());
        int majorVersion = Short.toUnsignedInt(buffer.getShort());
        // 校验版本兼容性
        checkClassVersion(majorVersion, minorVersion);
        //开辟并初始化常量池
        int constantPoolCount = Short.toUnsignedInt(buffer.getShort());
        ConstantPool constantPool = ConstantPool.readConstantPool(buffer,constantPoolCount);
        int accessFlags = Short.toUnsignedInt(buffer.getShort());
        //能在常量池里面找到本类的名字
        int thisClass   = Short.toUnsignedInt(buffer.getShort());
        //能在常量池里面找到超类的名字
        int superClass  = Short.toUnsignedInt(buffer.getShort());
        //interfaces 数组  //能在常量池里面找到所有实现接口的名字
        int interfacesCount = Short.toUnsignedInt(buffer.getShort());
        int[] interfaces = new int[interfacesCount];
        for (int i = 0; i < interfacesCount; i++) {
            //存的也只是一个常量池索引
            interfaces[i] = Short.toUnsignedInt(buffer.getShort());
        }
        //字段个数  我在这里读出字段个数是方便查看
        int fieldsCount = Short.toUnsignedInt(buffer.getShort());
        MemberInfo[] fields = MemberInfo.readMemberInfos(buffer, fieldsCount,constantPool);
        //方法个数  我在这里读出字段个数是方便查看
        //javap -v -cp D:\computerBookCode\jvmByJava\target\classes\com\zhu\classfile ClassBinary
        int methodsCount = Short.toUnsignedInt(buffer.getShort());
        MemberInfo[] methods = MemberInfo.readMemberInfos(buffer, methodsCount,constantPool);
        //属性的个数
        int attributesCount = Short.toUnsignedInt(buffer.getShort());
        AttributeInfo[] attributes = AttributeInfoFactory.readAttributes(buffer, constantPool,attributesCount);
        // 11. 构造并返回 ClassBinary 实例
        ClassBinary classBinary = new ClassBinary(magic, minorVersion, majorVersion, constantPoolCount, constantPool, accessFlags, thisClass, superClass,
                interfacesCount, interfaces, fieldsCount, fields, methodsCount, methods, attributesCount, attributes);
        return classBinary;
    }

    private static int readAndCheckMagic(ByteBuffer buffer) {
        int magic = buffer.getInt();
        if (magic != 0xCAFEBABE) {
            throw new ClassFormatError("Invalid magic:"+Integer.toHexString(magic));
        }
        return magic;
    }

    private static void checkClassVersion(int majorVersion, int minorVersion) {
        if (majorVersion == 45) {
            // minor 可为任意值
            return;
        } else if (majorVersion >= 46 && majorVersion <= 52) {
            if (minorVersion == 0) {
                return;
            }
        }
        throw new UnsupportedClassVersionError("Unsupported class version: " + majorVersion + "." + minorVersion);
    }

    public String getClassName(){
       return constantPool.getClassName(thisClass);
    }

    public String getSuperClassName(){
        return constantPool.getClassName(superClass);
    }

    public String[] getInterfaceNames(){
        String[] interfaceNames = new String[interfacesCount];
        for (int i=0;i<interfacesCount;i++){
             interfaceNames[i]=constantPool.getClassName(interfaces[i]);
        }
        return interfaceNames;
    }

    public int getMagic() {
        return magic;
    }

    public void setMagic(int magic) {
        this.magic = magic;
    }

    public int getMinorVersion() {
        return minorVersion;
    }

    public void setMinorVersion(int minorVersion) {
        this.minorVersion = minorVersion;
    }

    public int getMajorVersion() {
        return majorVersion;
    }

    public void setMajorVersion(int majorVersion) {
        this.majorVersion = majorVersion;
    }

    public int getConstantPoolCount() {
        return constantPoolCount;
    }

    public void setConstantPoolCount(int constantPoolCount) {
        this.constantPoolCount = constantPoolCount;
    }

    public ConstantPool getConstantPool() {
        return constantPool;
    }

    public void setConstantPool(ConstantPool constantPool) {
        this.constantPool = constantPool;
    }

    public int getAccessFlags() {
        return accessFlags;
    }

    public void setAccessFlags(int accessFlags) {
        this.accessFlags = accessFlags;
    }

    public int getThisClass() {
        return thisClass;
    }

    public void setThisClass(int thisClass) {
        this.thisClass = thisClass;
    }

    public int getSuperClass() {
        return superClass;
    }

    public void setSuperClass(int superClass) {
        this.superClass = superClass;
    }

    public int getInterfacesCount() {
        return interfacesCount;
    }

    public void setInterfacesCount(int interfacesCount) {
        this.interfacesCount = interfacesCount;
    }

    public int[] getInterfaces() {
        return interfaces;
    }

    public void setInterfaces(int[] interfaces) {
        this.interfaces = interfaces;
    }

    public int getFieldsCount() {
        return fieldsCount;
    }

    public void setFieldsCount(int fieldsCount) {
        this.fieldsCount = fieldsCount;
    }

    public MemberInfo[] getFields() {
        return fields;
    }

    public void setFields(MemberInfo[] fields) {
        this.fields = fields;
    }

    public int getMethodsCount() {
        return methodsCount;
    }

    public void setMethodsCount(int methodsCount) {
        this.methodsCount = methodsCount;
    }

    public MemberInfo[] getMethods() {
        return methods;
    }

    public void setMethods(MemberInfo[] methods) {
        this.methods = methods;
    }

    public int getAttributesCount() {
        return attributesCount;
    }

    public void setAttributesCount(int attributesCount) {
        this.attributesCount = attributesCount;
    }

    public AttributeInfo[] getAttributes() {
        return attributes;
    }

    public void setAttributes(AttributeInfo[] attributes) {
        this.attributes = attributes;
    }
}
